﻿using System;
using System.Drawing;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.SessionState;
using System.IO;
using System.Drawing.Imaging;

namespace Accumulation.Common {
    /// <summary>
    /// 随机数
    /// <remarks>
    /// MatchZhou - 2009/1/19
    /// </remarks>
    /// </summary>
    public class Randoms {
        /// <summary>
        /// 生成不重复的随机数，主要用来做随机数产生的种子
        /// </summary>
        /// <returns></returns>
        public static int RandomSeed() {
            int result = 0;
            Byte[] rngByte = new Byte[1];
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            for (int i = 0; i < 5; i++) {
                rng.GetBytes(rngByte);
                result += rngByte[0];
            }
            return result;
        }
        /// <summary>
        /// 生成指定长度的随机字符串（包含字母/数字/特殊字符）
        /// </summary>
        /// <param name="length">指定的长度</param>
        /// <returns>字符串</returns>
        public static string RandomString(int length) {
            return RandomString(length, RandomType.All);
        }
        /// <summary>
        /// 生成指定长度的随机字符串
        /// </summary>
        /// <param name="length">指定的长度</param>
        /// <param name="randomType">随机码生成的范围</param>
        /// <returns>字符串</returns>
        public static string RandomString(int length, RandomType randomType) {
            const string num = "0123456789";
            const string symbol = "~!@#$%^&()_=+{[}];',.";
            const string letter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

            string randomString = string.Empty;
            string returnValue = string.Empty;

            switch (randomType) {
                case RandomType.Letter:
                    randomString = letter;
                    break;
                case RandomType.LetterAndNum:
                    randomString = letter + num;
                    break;
                case RandomType.LetterAndSymbol:
                    randomString = letter + symbol;
                    break;
                case RandomType.Num:
                    randomString = num;
                    break;
                case RandomType.NumAndSymbol:
                    randomString = num + symbol;
                    break;
                case RandomType.Symobl:
                    randomString = symbol;
                    break;
                default:
                    randomString = letter + num + symbol;
                    break;
            }

            for (int i = 0; i < length; i++) {
                returnValue += randomString.Substring(new Random(RandomSeed()).Next(1, randomString.Length), 1);
            }

            return returnValue;
        }
        /// <summary>
        /// 生成指定长度的随机字符串（中文）
        /// </summary>
        /// <param name="length">长度</param>
        /// <returns>中文字符串</returns>
        public static string RandomStringZH(int length) {
            return RandomStringZH(length, string.Empty);
        }
        /// <summary>
        /// 生成指定长度的随机字符串（中文）
        /// </summary>
        /// <param name="length">长度</param>
        /// <param name="specifiedZHChars">汉字集，如果未指定则使用《GB2312简体中文编码表》</param>
        /// <returns>中文字符串</returns>
        public static string RandomStringZH(int length, string specifiedZHChars) {
            string returnValue = string.Empty;

            for (int i = 0; i < length; i++) {
                // 没有提供汉字集，则按照《GB2312简体中文编码表》来选择
                if (string.IsNullOrEmpty(specifiedZHChars)) {
                    byte[] charsByte = new byte[2];
                    // 高字节选择 \0xb0 - \0xf7
                    charsByte[0] = (byte)new Random(RandomSeed()).Next(0xb0, 0xf7);
                    // 低字节选择 \0xa1 - \0xfe
                    charsByte[1] = (byte)new Random(RandomSeed()).Next(0xa1, 0xfe);

                    returnValue += Encoding.GetEncoding("gb2312").GetString(charsByte);
                }
                // 指定了汉字集
                else {
                    returnValue += specifiedZHChars.Substring(
                        new Random(RandomSeed()).Next(1, specifiedZHChars.Length), 1);
                }
            }
            return returnValue;
        }
        /// <summary>
        /// 生成指定验证码的图片
        /// </summary>
        /// <param name="code">验证码字符</param>
        /// <returns>Bitmap</returns>
        public static Bitmap RandomImage(string code) {
            return RandomImage(code, true, null);
        }
        /// <summary>
        /// 生成指定验证码的图片
        /// </summary>
        /// <param name="code">验证码字符</param>
        /// <param name="isObstruction">干扰杂点</param>
        /// <returns>Bitmap</returns>
        public static Bitmap RandomImage(string code, bool isObstruction) {
            return RandomImage(code, isObstruction, null);
        }
        /// <summary>
        /// 生成指定验证码的图片
        /// </summary>
        /// <param name="code">验证码字符</param>
        /// <param name="isObstruction">干扰杂点</param>
        /// <param name="randomConfig">设置</param>
        /// <returns>Bitmap</returns>
        public static Bitmap RandomImage(string code, bool isObstruction, RandomConfig randomConfig) {
            // 未提供设置按按默认设置
            RandomConfig config = randomConfig ?? new RandomConfig {
                Padding = 4,
                BackgroundColor = Color.White,
                FontSize = 12, // pt
                FontColor = new Color[] { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple },
                FontFamily = new string[] { "Verdana", "Georgia" },
                ObstructionColor = Color.LightGray,
                ObstructionNum = 15
            };

            int imgWidth = code.Length * (config.FontSize + config.Padding) + config.Padding * 2;
            int imgHeight = config.FontSize * 2 + config.Padding * 2;

            Bitmap bitmap = new Bitmap(imgWidth, imgHeight);
            Graphics g = Graphics.FromImage(bitmap);
            g.Clear(config.BackgroundColor);
            // 给背景添加随机生成的燥点
            if (isObstruction) {
                Pen pen = new Pen(config.ObstructionColor, 0);
                for (int i = 0; i < code.Length * config.ObstructionNum; i++) {
                    Random rnd = new Random(RandomSeed());
                    g.DrawRectangle(pen, rnd.Next(imgWidth - 1), rnd.Next(imgHeight - 1), 1, 1);
                }
            }

            Font font;
            Brush brush;
            int colorIndex, fontIndex;
            int left = 0, top = 0, top1 = 1, top2 = 1;

            int n1 = (imgHeight - config.FontSize - config.Padding * 2);
            top1 = n1 / 4;
            top2 = (n1 / 4) * 2;

            for (int i = 0; i < code.Length; i++) {
                // 随机字体和颜色的验证码字符
                Random rnd = new Random(RandomSeed());
                colorIndex = rnd.Next(config.FontColor.Length - 1);
                fontIndex = rnd.Next(config.FontFamily.Length - 1);

                font = new Font(config.FontFamily[fontIndex], config.FontSize, FontStyle.Bold);
                brush = new SolidBrush(config.FontColor[colorIndex]);
                top = i % 2 == 1 ? top2 : top1;
                left = i * (config.FontSize + config.Padding);
                g.DrawString(code.Substring(i, 1), font, brush, left, top);
            }
            // 画一个边框 边框颜色为 Color.Gainsboro
            g.DrawRectangle(new Pen(Color.Gainsboro, 0), 0, 0, imgWidth - 1, imgHeight - 1);
            g.Dispose();

            return bitmap;
        }
        /// <summary>
        /// 输出随机码，产生默认 4 位长度的 字母图片，返回的 Session 对象键是 RandomString
        /// </summary>
        /// <param name="httpContext">当前的 HttpContext 对象</param>
        public static void OutputRadomImage(HttpContext httpContext) {
            OutputRadomImage(httpContext, 4);
        }
        /// <summary>
        /// 输出随机码，产生指定长度字母图片，返回的 Session 对象键是 RandomString
        /// </summary>
        /// <param name="httpContext">当前的 HttpContext 对象</param>
        /// <param name="randomCodeLength">随机码长度</param>
        public static void OutputRadomImage(HttpContext httpContext, int randomCodeLength) {
            string randomCode = RandomString(randomCodeLength, RandomType.Letter);
            OutputRadomImage(httpContext, randomCode, "RandomString", null);
        }
        /// <summary>
        /// 输出随机码，产生指定字符图片，返回的 Session 对象键是 RandomString
        /// </summary>
        /// <param name="httpContext">当前的 HttpContext 对象</param>
        /// <param name="randomCode">指定字符</param>
        public static void OutputRadomImage(HttpContext httpContext, string randomCode) {
            OutputRadomImage(httpContext, randomCode, "RandomString", null);
        }
        /// <summary>
        /// 输出随机码
        /// </summary>
        /// <param name="httpContext">当前的 HttpContext 对象</param>
        /// <param name="randomCode">指定字符</param>
        /// <param name="sessionName">指定 Session 键名</param>
        /// <param name="randomConfig">设置</param>
        public static void OutputRadomImage(HttpContext httpContext, string randomCode, string sessionName, RandomConfig randomConfig) {
            HttpSessionState session = httpContext.Session;
            HttpResponse response = httpContext.Response;
            // Session 保存
            session.Add(sessionName, randomCode);
            // 图片存入内存流
            Bitmap bitmap = RandomImage(randomCode, true, randomConfig);
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, ImageFormat.Gif);
            // 输出
            response.Clear();
            response.ContentType = "image/gif";
            response.BinaryWrite(ms.GetBuffer());
            // 资源释放
            bitmap.Dispose();
            ms.Close();
            ms.Dispose();
        }
    }
}
